#ifndef EQNPARSER_H
#define EQNPARSER_H

#include <ctype.h>
#include "QuickArray.h"
#include "tool.h"
#include "Operation.h"

#define MAX_LEN 1024
#define MAX_REGS 29

typedef QuickArray<float,MAX_REGS> REGSET;
typedef QuickArray<char,100> OPSTACK;
typedef QuickArray<char,100> VARSTACK;

class EqnParser
{
    enum OPS{PLUS='+',MINUS='-',MULTIPLY='*',DIVIDE='/',POW='^',MOD='%',BRACK='(',CBRACK=')'};
    enum FUNCS{SIN='a', COS='b', TAN='c',SQRT='d',FABS='e'};

    QuickArray<char,MAX_LEN> RPN;
    REGSET registers;
    char eqnStr[MAX_LEN];

    //token parsers//////////////////////////////////////////////////////
    bool isFunc(char c){return c==SIN || c==COS || c==TAN || c==SQRT || c==FABS;}
    bool isOperator(char c){return c == PLUS || c==MINUS || c==MULTIPLY || c==DIVIDE || c==POW || c==MOD;}

    bool isOperation(char c){ return isOperator(c) || isFunc(c); }

    bool isVar(char c){return  c=='[' || c=='\\' || c==']'; }

    bool isSymbol(char c){ return isupper(c) || isVar(c); }

    bool isVariable(char* p){
        return isSymbol(*p++) && !isalnum(*p);
    }

    bool isReg(char c){return isupper(c);}

    int isFn(char* p){
        return isFunc(*p) && *(p+1)=='(';
    }
    ////////////////////////////////////////////////////////////////////

    //utilities///////////////////////////////////////
    int prec(char op){
        switch(op){
            case '+':
            case '-':
                return 0;
            case '/':
            case '*':
            case '^':
                return 1;
        case SIN:
        case COS:
        case TAN:
        case SQRT:
        case FABS:
                return 2;
            default:
                return -1;
        }
    }

    int checkAhead(char* src, const char* target)
    {
        int i=0;
        while(*src!='\0' && *target!='\0' && *src++ == *target++)++i;
        return *target=='\0'? i : 0;
    }

    void clearAhead(char* p, int n){while(n--)*p++=' ';}

    int toLoc(int n)
    {return n-'A';}

    void output()
    {printf("%s\n",RPN.ptr());fflush(stdout);}

    ////////////////////////////////////////////////////

    void setVarX(int n)
    {
        registers[toLoc('[')]=n;
    }

    void setVarY(int n)
    {
        registers[toLoc('\\')]=n;
    }

    void setVarZ(int n)
    {
        registers[toLoc(']')]=n;
    }

    void processEqn(char* str);
    int recDescent(char* eqn);
    float evalRPN(char* rpn);
public:
    EqnParser(const char *eqn);
    float eval(int x=0, int y=0, int z=0);
};

#endif // EQNPARSER_H
